package app.services.impl;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import app.models.BiometriaTemplate;
import app.models.Consulta;
import app.models.Especialidade;
import app.models.Paciente;
import app.models.enums.StatusBiometria;
import app.models.enums.StatusConsulta;
import app.models.enums.TipoConsulta;
import app.repositories.ConsultaRepository;
import app.repositories.Repository;
import app.services.BiometriaTemplateService;
import app.services.ConsultaService;
import app.services.EspecialidadeService;
import app.services.ParametroSistemasService;
import app.services.SubEspecialidadeService;
import app.util.Constants;
import app.util.DateUtils;
import br.com.caelum.vraptor.ioc.Component;

import com.digitalpersona.onetouch.DPFPFeatureSet;
import com.digitalpersona.onetouch.DPFPGlobal;
import com.digitalpersona.onetouch.DPFPTemplate;
import com.digitalpersona.onetouch._impl.DPFPTemplateFactoryImpl;
import com.digitalpersona.onetouch.verification.DPFPVerification;
import com.digitalpersona.onetouch.verification.DPFPVerificationResult;

@Component
public class ConsultaServiceImpl extends GenericServiceImpl<Consulta, Long> implements ConsultaService {

	private final ConsultaRepository repository;

	private final BiometriaTemplateService biometriaService;
	
	private final EspecialidadeService especialidadeService;
	
	private final SubEspecialidadeService subEspecialidadeService;
	
	private final ParametroSistemasService parametroSistemasService;
	
	
	@SuppressWarnings("unchecked")
	public ConsultaServiceImpl(ConsultaRepository repository, BiometriaTemplateService biometriaService, 
			EspecialidadeService especialidadeService, SubEspecialidadeService subEspecialidadeService, 
				ParametroSistemasService parametroSistemasService) {
		this.repository = repository;
		this.biometriaService = biometriaService;
		this.especialidadeService = especialidadeService;
		this.subEspecialidadeService = subEspecialidadeService;
		this.parametroSistemasService = parametroSistemasService;
		super.setGenericRepository((Repository<Consulta, Long>) repository);
	}

	public List<Consulta> findAllByFilter(String filter) {
		return repository.findAllByFilter(filter);
	}
	
	public Consulta replaceCommaForPoint(Consulta consulta) {
		
		if(consulta.getDescontoString() != null && !consulta.getDescontoString().isEmpty()){
			consulta.setDesconto(Double.valueOf(consulta.getDescontoString().replaceAll(",", ".")));
		}
		
		return consulta;
	}
	
	public void cancelConsulta(Integer idConsulta){
		Consulta consulta = find(idConsulta.longValue());
		consulta.setStatus(StatusConsulta.CANCELADA);

		update(consulta);
	}
	
	public Consulta setStatusConsultaBeforeSave(Consulta consulta) {
		if(consulta.isFlagPagou()) {
			consulta.setDataPagamento(DateUtils.getDataAtual());
			consulta.setStatus(StatusConsulta.PAGA);
		} else {
			consulta.setStatus(StatusConsulta.MARCADA);
		}
		
		return consulta;
	}
	
	public Consulta setValueAndDiscount(Consulta consulta){
		
		if(consulta.getDesconto() != null){
			consulta.setValor(consulta.getValor() - consulta.getDesconto());
		} else {
			consulta.setValor(consulta.getValor());
		}
		
		return consulta;
	}
	
	public Integer findLastMonthDay(Integer month, Integer year) {
		
		// dia da semana que está o dia 1 do mês atual
		Calendar cal = Calendar.getInstance();
		
		if (month != null) {
			cal.set(Calendar.MONTH, month);
		}
		
		if (year != null) {
			cal.set(Calendar.YEAR, year);
		}
		
        int ultimoDiaMes = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
        
        return ultimoDiaMes;
	}
	
	public Integer findFirstWeekDay(Integer month, Integer year){
		
		// dia da semana que está o dia 1 do mês atual
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.DATE, 1);
		
		if (month != null) {
			cal.set(Calendar.MONTH, month);
		}
		
		if (year != null) {
			cal.set(Calendar.YEAR, year);
		}
		
        int inicioMesCal = cal.get(Calendar.DAY_OF_WEEK);
		
        return inicioMesCal;
	}

	public List<Consulta> findConsultasByDate(Integer day, Integer month, Integer year) {
		return repository.findConsultasByDate(day, month, year);
	}
	
	public Boolean validateMaxConsultNumberByHour(Consulta consulta){
		Especialidade especialidade;
		Integer numConsultas;
		
		if(consulta.getIdEspecialidade() != null){
			especialidade = especialidadeService.find(consulta.getIdEspecialidade().longValue());
		} else {
			especialidade = subEspecialidadeService.find(consulta.getIdSubEspecialidade().longValue()).getEspecialidade();
			consulta.setIdEspecialidade(especialidade.getId().intValue());
		}
		
		numConsultas = repository.findConsultasByHourAndDateAndByEspecialidade(consulta).size();
		
		if(numConsultas < especialidade.getQtdMaximaPacientesPorHora()){
			return false;
		} else {
			return true;
		}
		
	}
	
	public List<Consulta> findConsultasByFilters(Consulta consulta, Integer mDay, Integer month, Integer year) {

		List<Consulta> consultas = new ArrayList<Consulta>();
		
		try {
			if (null != mDay && null != month && null != year) {
				String dataBR = mDay + "/" + (month + 1) + "/" + year;
				String dataUS = DateUtils.convertDataBRtoUS(dataBR);
				
				consulta.setData(dataUS);
			}
			
			consultas = repository.findConsultasByFilters(consulta);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		return consultas;
	}
	
	public Map<Integer, List<Consulta>> findConsultasInHour(Consulta consulta, Integer mDay, Integer month, Integer year) {
		
		List<Consulta> consultas = new ArrayList<Consulta>();
		List<Consulta> consultasPorHora = new ArrayList<Consulta>();
		Map<Integer, List<Consulta>> consultasDiarias = new HashMap<Integer, List<Consulta>>();
		
		try {
			if (null != mDay && null != month && null != year) {
				String dataBR = mDay + "/" + (month + 1) + "/" + year;
				String dataUS = DateUtils.convertDataBRtoUS(dataBR);
				
				consulta.setData(dataUS);
			}
			
			consultas = repository.findConsultasByFilters(consulta);
			
			if (consultas != null) {
				for (int i = 8; i <= 20; i++) {
					for (Consulta c : consultas) {
						int p = c.getHoraInicial().indexOf(":");
						int h = Integer.valueOf(c.getHoraInicial().substring(0, p));
						
						if (i == h) {
							consultasPorHora.add(c);
						}
					}
					
					consultasDiarias.put(i, consultasPorHora);
					consultasPorHora = new ArrayList<Consulta>();
				}
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		return consultasDiarias;
	}

	public List<Consulta> findLastConsultas() {
		return repository.findLastConsultas();
	}

	public List<Consulta> findNextConsultas() {
		return repository.findNextConsultas();
	}

	public Map<Integer, Integer> findConsultasInDayOnMonth(Consulta consulta, Integer month, Integer year) {
		
		Integer qntDias = findLastMonthDay(month, year);
		Map<Integer, Integer> consultasDiarias = new HashMap<Integer, Integer>();
		
		for (int i = 1; i <= qntDias; i++) {
			String dataStr = year + "-" + (month + 1) + "-" + i;
			
			Integer consultasDia = repository.findConsultasInDayOnMonth(consulta, dataStr);
			
			consultasDiarias.put(i, consultasDia);
		}
	
		return consultasDiarias;
	}
	
	public Paciente findPacienteByConsultaId(Integer id){
		return repository.findPacienteByConsultaId(id.longValue());
	}
	
	public boolean matchBiometriaTemplates(Integer id, String finger, DPFPFeatureSet featureSet){
		Boolean verify = false;
		
		BiometriaTemplate biometriaTemplate = biometriaService.
				findBiometriaTemplateByPacienteIdAndByFinger(id, finger);
		
		DPFPTemplateFactoryImpl templateFactory = new DPFPTemplateFactoryImpl();
		
		if(biometriaTemplate != null) {
		
			DPFPTemplate template = templateFactory.createTemplate(biometriaTemplate.getTemplate());
			
			DPFPVerification matcher = DPFPGlobal.getVerificationFactory().createVerification();
			
			matcher.setFARRequested(DPFPVerification.MEDIUM_SECURITY_FAR);
					
			DPFPVerificationResult result = matcher.verify(featureSet, template);
			if (result.isVerified()) {
				verify = true;
			}
		}

		return verify;
		
	}
	
	public boolean verifyLatePayments(Integer pacienteId){
		Boolean verify = false;
		
		List<Consulta> consultaList = repository.findLatePaymentsByPacienteId(pacienteId);
		
		Integer numMaxPermitido = Integer.valueOf(parametroSistemasService.findByParam(Constants.NUM_MAX_CONSULTAS_DEVEDORES_PERMITIDO).getValor());
		
		if(consultaList.size() > numMaxPermitido){
			verify = true;
		}
		
		return verify;
	}
	
	public boolean verifyPacientesConsults(Consulta consulta){
		boolean verify = false;
		
		if(repository.findConsultaByPacienteIdAndByTimeAndByDate(consulta) != null){
			verify = true;
		}
		
		return verify;
	}
	
	public boolean verifyEspecialistasConsults(Consulta consulta){
		boolean verify = false;
		
		if(repository.findConsultaByEspecialistaIdAndByTimeAndByDate(consulta) != null){
			verify = true;
		}
		
		return verify;
	}

	// Quantidade de consultas diárias
	public Integer amountConsultasToday() {
		Integer today = DateUtils.getDiaAtual();
		Integer month = DateUtils.getMesAtual();
		Integer year = DateUtils.getAnoAtual();
		
		List<Consulta> consultas = repository.findConsultasByDate(today, month, year);
				
		return consultas.size();
	}

	
	// Quantidade de consultas semanais
	public Integer amountConsultasWeek() {
		Integer today = DateUtils.getDiaAtual();
		Integer month = DateUtils.getMesAtual();
		Integer year = DateUtils.getAnoAtual();
		
		// dia da semana que está o dia 1 do mês atual
		Calendar cal = Calendar.getInstance();

		cal.set(Calendar.DATE, today);
		cal.set(Calendar.MONTH, month - 1);
		cal.set(Calendar.YEAR, year);
		
		DateFormat df = new SimpleDateFormat ("yyyy-MM-dd");
        cal.setFirstDayOfWeek(Calendar.SUNDAY);
        int diaSemana = cal.get(Calendar.DAY_OF_WEEK);
        
        cal.add(Calendar.DAY_OF_MONTH, Calendar.SUNDAY - diaSemana);  
        String dataInicio = df.format(cal.getTime());
        
        cal.add(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); 
        String dataFim = df.format(cal.getTime());
        
		List<Consulta> consultas = repository.consultasBetweenDates(dataInicio, dataFim);
		
		return consultas.size();
	}

	
	// Quantidade de consultas mensais
	public Integer amountConsultasMonth() {
		Integer month = DateUtils.getMesAtual();
		Integer year = DateUtils.getAnoAtual();
		
		try {
			Calendar cal = Calendar.getInstance();
			cal.set(Calendar.DATE, 1);
			cal.set(Calendar.MONTH, month - 1);
			cal.set(Calendar.YEAR, year);
			
			String dataInicio = DateUtils.convertDateToDataStr(cal.getTime());
			
			cal.set(Calendar.DATE, findLastMonthDay(month, year));
			cal.set(Calendar.MONTH, month - 1);
			cal.set(Calendar.YEAR, year);
			
			String dataFim = DateUtils.convertDateToDataStr(cal.getTime());
			
			
			List<Consulta> consultas = repository.consultasBetweenDates(dataInicio, dataFim);
			
			return consultas.size();
			
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	public Integer amountPcntConsultasToday(TipoConsulta tipo) {
		Integer today = DateUtils.getDiaAtual();
		Integer month = DateUtils.getMesAtual();
		Integer year = DateUtils.getAnoAtual();
		
		Calendar cal = Calendar.getInstance();

		cal.set(Calendar.DATE, today);
		cal.set(Calendar.MONTH, month - 1);
		cal.set(Calendar.YEAR, year);
		
		DateFormat df = new SimpleDateFormat ("yyyy-MM-dd");
		String hoje = df.format(cal.getTime());
		
		Consulta consulta = new Consulta();
		consulta.setTipo(tipo);
		
		consulta.setStatus(null);
		Integer consultasMarcadas = repository.findConsultasInDayOnMonth(consulta, hoje);

		consulta.setStatus(StatusConsulta.FINALIZADA);
		Integer consultasFinalizadas = repository.findConsultasInDayOnMonth(consulta, hoje);
		
		Integer pcntFinal = 0;
		
		if (consultasFinalizadas > 0) {
			pcntFinal = (consultasFinalizadas * 100) / consultasMarcadas;
		}
		
		return pcntFinal;
	}

	public Integer amountPcntConsultasWeek(TipoConsulta tipo) {
		Integer today = DateUtils.getDiaAtual();
		Integer month = DateUtils.getMesAtual();
		Integer year = DateUtils.getAnoAtual();
		
		// dia da semana que está o dia 1 do mês atual
		Calendar cal = Calendar.getInstance();

		cal.set(Calendar.DATE, today);
		cal.set(Calendar.MONTH, month - 1);
		cal.set(Calendar.YEAR, year);
		
		DateFormat df = new SimpleDateFormat ("yyyy-MM-dd");
        cal.setFirstDayOfWeek(Calendar.SUNDAY);
        int diaSemana = cal.get(Calendar.DAY_OF_WEEK);
        
        cal.add(Calendar.DAY_OF_MONTH, Calendar.SUNDAY - diaSemana);  
        String dataInicio = df.format(cal.getTime());
        
        cal.add(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); 
        String dataFim = df.format(cal.getTime());
        
		List<Consulta> consultas = new ArrayList<Consulta>();
		Consulta consulta = new Consulta();
		consulta.setTipo(tipo);
		
		consulta.setStatus(null);
		consultas = repository.consultasBetweenDatesAndFilter(consulta, dataInicio, dataFim);
		Integer consultasMarcadas = consultas.size();
		
		consulta.setStatus(StatusConsulta.FINALIZADA);
		consultas = repository.consultasBetweenDatesAndFilter(consulta, dataInicio, dataFim);
		Integer consultasFinalizadas = consultas.size();
		
		Integer pcntFinal = 0;
		
		if (consultasFinalizadas > 0) {
			pcntFinal = (consultasFinalizadas * 100) / consultasMarcadas;
		}
		
		return pcntFinal;
	}

	public Integer amountPcntConsultasMonth(TipoConsulta tipo) {
		Integer month = DateUtils.getMesAtual();
		Integer year = DateUtils.getAnoAtual();
		
		try {
			Calendar cal = Calendar.getInstance();
			cal.set(Calendar.DATE, 1);
			cal.set(Calendar.MONTH, month - 1);
			cal.set(Calendar.YEAR, year);
			
			String dataInicio = DateUtils.convertDateToDataStr(cal.getTime());
			
			cal.set(Calendar.DATE, findLastMonthDay(month, year));
			cal.set(Calendar.MONTH, month - 1);
			cal.set(Calendar.YEAR, year);
			
			String dataFim = DateUtils.convertDateToDataStr(cal.getTime());
			
			List<Consulta> consultas = new ArrayList<Consulta>();
			Consulta consulta = new Consulta();
			consulta.setTipo(tipo);
			
			consulta.setStatus(null);
			consultas = repository.consultasBetweenDatesAndFilter(consulta, dataInicio, dataFim);
			Integer consultasMarcadas = consultas.size();
			
			consulta.setStatus(StatusConsulta.FINALIZADA);
			consultas = repository.consultasBetweenDatesAndFilter(consulta, dataInicio, dataFim);
			Integer consultasFinalizadas = consultas.size();
			
			Integer pcntFinal = 0;
			
			if (consultasFinalizadas > 0) {
				pcntFinal = (consultasFinalizadas * 100) / consultasMarcadas;
			}
			
			return pcntFinal;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
		return null;
	}

	public List<Consulta> findAllForTask() throws ParseException {
		return repository.findAllForTask();
	}

	public Boolean payConsulta(Integer idConsulta) {
		
		Consulta consulta = find(idConsulta.longValue());
		
		if(StatusConsulta.MARCADA.equals(consulta.getStatus())){
			consulta.setStatus(StatusConsulta.PAGA);
			consulta.setDataPagamento(DateUtils.getDataAtual());
		} else if(StatusConsulta.REALIZADA.equals(consulta.getStatus())){
			consulta.setStatus(StatusConsulta.FINALIZADA);
			consulta.setDataPagamento(DateUtils.getDataAtual());
		} else {
			return true;
		}
		
		update(consulta);
		
		return false;
	}
	
	public void startConsulta(Consulta consulta){
		
		if(consulta.isFlagPagou() || StatusConsulta.PAGA.equals(consulta.getStatus())){
			consulta.setStatus(StatusConsulta.FINALIZADA);
		} else {
			consulta.setStatus(StatusConsulta.REALIZADA);
		}
		
		update(consulta);
	}
	
	public void verifyIfBiometriaIsValid(Boolean verify, Integer idConsulta){
		
		Consulta consulta = find(idConsulta.longValue());
		
		if(verify){
			consulta.setStatusBiometria(StatusBiometria.SIM);
			update(consulta);
		}
	}
	
	public boolean validateIfDateIsGreaterEqualToday(Integer mDay, Integer month, Integer year) {
		boolean validarDataSelecionada = false;
		
		Calendar dateSelected = Calendar.getInstance();
		dateSelected.set(year, month, mDay, 0, 0, 0);
		
		Calendar today = Calendar.getInstance();
		today.set(DateUtils.getAnoAtual(), DateUtils.getMesAtual(), DateUtils.getDiaAtual(), 0, 0, 0);
		
		if (dateSelected.after(today) || dateSelected.equals(today)) {
			validarDataSelecionada = true;
		}
		
		return validarDataSelecionada;
	}
}
